﻿using NetSync.Core;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace NetSync.Rabbitmq
{
    public abstract class NetSyncAbstract<TRequest, TResponse> : INetSync<TRequest, TResponse> where TRequest : class, new() where TResponse : class, new()
    {
        private readonly IWaitSync _waitSync;

        public NetSyncAbstract(INetConnection connection, IWaitSync waitSync)
        {
            NetConnection = connection ?? throw new ArgumentNullException(nameof(connection));
            _waitSync = waitSync ?? throw new ArgumentNullException(nameof(waitSync));
            SetConnectionMessageEvent();
        }

        public INetConnection NetConnection { get; }

        /// <summary>
        /// 消息唯一ID，请求和回复保持一致； 
        /// 请求时，TResponse为null；回复时,TRequest为null
        /// </summary>
        public abstract Func<TRequest, TResponse, string> MsgIdFunc { get; }

        public async Task<TResponse1> SendReplyAsync<TResponse1>(string topic, object request, int timeoutSecond = 120, CancellationToken token = default)
            where TResponse1 : TResponse, new()
        {
            if (request == null || !(request is TRequest reqData)) throw new ArgumentNullException(nameof(request));
            var result = await _waitSync.WaitAsync<byte[]>(MsgIdFunc?.Invoke(reqData, null), () => NetConnection.PublishAsync(topic, request, default), timeoutSecond);
            return await (NetConnection as NetRabbitMQConnection).NetSerialize.DeSerializeAsync<TResponse1>(result);
        }

        public void Dispose()
        {
            NetConnection.DispatchMessageEvent -= MessageHandler;
        }

        #region private methods
        private async void MessageHandler(object sender, DispatchEventArgs e)
        {
            var bytes = e.Message.ToArray();
            var result = await (NetConnection as NetRabbitMQConnection).NetSerialize.DeSerializeAsync<TResponse>(bytes);
            e.IsHandled = await _waitSync.ReleaseAsync(MsgIdFunc?.Invoke(null, result), bytes);
        }

        private void SetConnectionMessageEvent()
        {
            var funcs = (NetConnection as NetRabbitMQConnection).ListEventFunc(nameof(NetConnection.DispatchMessageEvent), true);
            NetConnection.DispatchMessageEvent += MessageHandler;
            foreach (var func in funcs)
            {
                NetConnection.DispatchMessageEvent += func as EventHandler<DispatchEventArgs>;
            }

        }
        #endregion
    }
}
